package app.modules.security.business.service.permission;

import app.modules.common.constant.CommonLockConstants;
import app.modules.security.business.service.SyncRbacCacheService;
import app.modules.security.constant.SecurityLockConstants;
import app.modules.security.constant.SecurityMsgConstants;
import app.modules.security.dto.web.permission.WebPermissionDTO;
import app.modules.security.dto.web.permission.WebPermissionQueryDTO;
import app.modules.security.dto.web.permission.WebPermissionSaveDTO;
import app.modules.security.dto.web.permission.WebPermissionUpdateDTO;
import app.modules.security.persistence.dao.FunctionPermissionRelationDAO;
import app.modules.security.persistence.dao.PermissionDAO;
import app.modules.security.persistence.dao.PermissionResourceRelationDAO;
import app.modules.security.util.conversion.PermissionConvertUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jasmine.framework.common.exception.BusinessException;
import jasmine.framework.common.exception.DataNotFoundException;
import jasmine.framework.common.util.CheckUtil;
import jasmine.framework.common.util.MapperUtil;
import jasmine.framework.common.util.NewUtil;
import jasmine.framework.lock.annotation.DistributedLock;
import jasmine.security.rbac.model.SecPermission;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;

/**
 * @author mh.z
 */
@Service
public class PermissionService {
    private PermissionDAO permissionDAO;
    private PermissionResourceRelationDAO permissionResourceRelationDAO;
    private FunctionPermissionRelationDAO functionPermissionRelationDAO;
    private SyncRbacCacheService syncRbacCacheService;

    public PermissionService(PermissionDAO permissionDAO,
                             PermissionResourceRelationDAO permissionResourceRelationDAO,
                             FunctionPermissionRelationDAO functionPermissionRelationDAO,
                             SyncRbacCacheService syncRbacCacheService) {
        this.permissionDAO = permissionDAO;
        this.permissionResourceRelationDAO = permissionResourceRelationDAO;
        this.functionPermissionRelationDAO = functionPermissionRelationDAO;
        this.syncRbacCacheService = syncRbacCacheService;
    }

    /**
     * 保存权限
     *
     * @param saveDTO
     * @return
     */
    @DistributedLock(category = SecurityLockConstants.LOCK_CATEGORY_CRUD_PERMISSION, key = CommonLockConstants.LOCK_KEY_ALL)
    @Transactional(rollbackFor = Exception.class)
    public WebPermissionDTO savePermission(WebPermissionSaveDTO saveDTO) {
        CheckUtil.notNull(saveDTO, "saveDTO null");

        String permissionCode = saveDTO.getPermissionCode();
        long permissionCodeCount = permissionDAO.countByPermissionCode(permissionCode, null);
        if (permissionCodeCount > 0) {
            throw new BusinessException(SecurityMsgConstants.THE_PERMISSION_CODE_ALREADY_EXISTS,
                    NewUtil.array(permissionCode));
        }

        SecPermission secPermission = new SecPermission();
        MapperUtil.mapFields(saveDTO, secPermission);
        permissionDAO.save(secPermission);

        return getWebPermissionDtoById(secPermission.getId());
    }

    /**
     * 更新权限
     *
     * @param updateDTO
     * @return
     */
    @DistributedLock(category = SecurityLockConstants.LOCK_CATEGORY_CRUD_PERMISSION, key = CommonLockConstants.LOCK_KEY_ALL)
    @Transactional(rollbackFor = Exception.class)
    public WebPermissionDTO updatePermission(WebPermissionUpdateDTO updateDTO) {
        CheckUtil.notNull(updateDTO, "updateDTO null");
        Long recordId = updateDTO.getId();

        String permissionCode = updateDTO.getPermissionCode();
        long permissionCodeCount = permissionDAO.countByPermissionCode(permissionCode,
                Collections.singletonList(recordId));
        if (permissionCodeCount > 0) {
            throw new BusinessException(SecurityMsgConstants.THE_PERMISSION_CODE_ALREADY_EXISTS,
                    NewUtil.array(permissionCode));
        }

        SecPermission secPermission = permissionDAO.getById(recordId);
        if (secPermission == null) {
            throw new DataNotFoundException(SecPermission.class, recordId);
        }

        MapperUtil.mapFields(updateDTO, secPermission);
        permissionDAO.strictUpdateById(secPermission);

        return getWebPermissionDtoById(secPermission.getId());
    }

    /**
     * 删除指定 ID 的记录
     *
     * @param recordId
     */
    @DistributedLock(category = SecurityLockConstants.LOCK_CATEGORY_CRUD_PERMISSION, key = CommonLockConstants.LOCK_KEY_ALL)
    @Transactional(rollbackFor = Exception.class)
    public void deleteRecordById(Long recordId) {
        CheckUtil.notNull(recordId, "recordId null");

        permissionDAO.deleteById(recordId);
        permissionResourceRelationDAO.deleteByPermissionId(recordId);
        functionPermissionRelationDAO.deleteByPermissionId(recordId);

        // 同步缓存
        syncRbacCacheService.syncFunctionsWithResourceIdCache(SecPermission.class, recordId);
    }

    /**
     * 分页查询权限
     *
     * @param queryDTO
     * @param page
     * @return
     */
    public List<WebPermissionDTO> pageWebPermissionDTOsByCond(WebPermissionQueryDTO queryDTO, Page page) {
        CheckUtil.notNull(queryDTO, "queryDTO null");
        CheckUtil.notNull(page, "page null");

        List<WebPermissionDTO> recordList = permissionDAO.pageWebPermissionDTOsByCond(queryDTO, page);

        return recordList;
    }

    /**
     * 查询指定 ID 的权限
     *
     * @param id
     * @return
     */
    public WebPermissionDTO getWebPermissionDtoById(Long id) {
        SecPermission secPermission = permissionDAO.getById(id);
        if (secPermission == null) {
            throw new DataNotFoundException(SecPermission.class, id);
        }

        WebPermissionDTO webPermissionDTO = PermissionConvertUtil.secPermission2WebPermissionDTO(secPermission);

        return webPermissionDTO;
    }

}
